package org.pay.pay.ctrl.payment;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.pay.core.common.constant.MchConstant;
import org.pay.core.common.constant.PayConstant;
import org.pay.core.common.constant.PayEnum;
import org.pay.core.common.util.MyLog;
import org.pay.core.common.util.MySeq;
import org.pay.core.common.util.PayUtil;
import org.pay.core.entity.*;
import org.pay.pay.channel.PaymentInterface;
import org.pay.pay.service.RpcCommonService;
import org.pay.pay.util.SpringUtil;

import java.math.BigDecimal;

/**
 * @Description: 支付订单,包括:统一下单,订单查询,补单等接口
 * @author yangshancheng jmdhappy@126.com
 * @date 2017-07-05
 * @version V1.0
 * @Copyright: www.pay.org
 */
@RestController
public class PayOrderController {

    private final MyLog _log = MyLog.getLog(PayOrderController.class);

    @Autowired
    private RpcCommonService rpcCommonService;

    private PaymentInterface paymentInterface;

    /**
     * 统一下单接口:
     * 1)先验证接口参数以及签名信息
     * 2)验证通过创建支付订单
     * 3)根据商户选择渠道,调用支付服务进行下单
     * 4)返回下单数据
     * @param params
     * @return
     */
    @RequestMapping(value = "/api/pay/create_order")
    public String payOrder(@RequestParam String params) {
        _log.info("###### 开始接收商户统一下单请求 ######");
        String logPrefix = "【商户统一下单】";
        _log.info("{}请求参数:{}", logPrefix, params);
        try {
            JSONObject po = JSONObject.parseObject(params);
            JSONObject payContext = new JSONObject();
            PayOrder payOrder = null;
            // 验证参数有效性
            Object object = validateParams(po, payContext);
            if (object instanceof String) {
                _log.info("{}参数校验不通过:{}", logPrefix, object);
                return PayUtil.makeRetFail(PayUtil.makeRetMap(PayConstant.RETURN_VALUE_FAIL, object.toString(), null, PayEnum.ERR_0014.getCode(), object.toString()));
            }
            if (object instanceof PayOrder) payOrder = (PayOrder) object;
            if(payOrder == null) return PayUtil.makeRetFail(PayUtil.makeRetMap(PayConstant.RETURN_VALUE_FAIL, "支付中心下单失败", null, PayEnum.ERR_0010.getCode(), "生成支付订单失败"));

            // 判断是否已经下过单 如果已经下过单，就不需要重复下单
            PayOrder existOrder = rpcCommonService.rpcPayOrderService.baseSelectByMchOrderNo(payOrder.getMchOrderNo());
            if(existOrder != null){
                payOrder.setPayOrderId(existOrder.getPayOrderId());
            }else{
                int result = rpcCommonService.rpcPayOrderService.baseCreatePayOrder(payOrder);
                _log.info("{}创建支付订单,结果:{}", logPrefix, result);
                if(result != 1) {
                    return PayUtil.makeRetFail(PayUtil.makeRetMap(PayConstant.RETURN_VALUE_FAIL, "支付中心下单失败", null, PayEnum.ERR_0010.getCode(), "DB插入支付订单失败"));
                }
            }

            String channelId = payOrder.getChannelId();
            String channelName = channelId.substring(0, channelId.indexOf("_"));
            try {
                paymentInterface = (PaymentInterface) SpringUtil.getBean(channelName.toLowerCase() +  "PaymentService");
            }catch (BeansException e) {
                _log.error(e, "");
                return PayUtil.makeRetFail(PayUtil.makeRetMap(PayConstant.RETURN_VALUE_FAIL, "调用支付渠道失败", null, PayEnum.ERR_0010.getCode(), "支付渠道类型[channelId="+channelId+"]实例化异常"));
            }
            JSONObject retObj = paymentInterface.pay(payOrder);
            if(retObj.get(PayConstant.RETURN_PARAM_RETCODE).equals(PayConstant.RETURN_VALUE_SUCCESS)) {
                retObj.put("payOrderId", payOrder.getPayOrderId());
                return PayUtil.makeRetData(retObj, payContext.getString("key"));
            }else {
                return PayUtil.makeRetFail(PayUtil.makeRetMap(PayConstant.RETURN_VALUE_FAIL, "调用支付渠道失败", null, retObj.getString("errCode"), retObj.getString("errDes")));
            }
        }catch (Exception e) {
            _log.error(e, "");
            return PayUtil.makeRetFail(PayUtil.makeRetMap(PayConstant.RETURN_VALUE_FAIL, "支付中心系统异常", null, PayEnum.ERR_0010.getCode(), "请联系技术人员查看"));
        }
    }

    /**
     * 验证创建订单请求参数,参数通过返回JSONObject对象,否则返回错误文本信息
     * @param params
     * @return
     */
    private Object validateParams(JSONObject params, JSONObject payContext) {
        // 验证请求参数,参数有问题返回错误提示
        String errorMessage;
        // 支付参数
        String mchId = params.getString("mchId"); 			    // 商户ID
        String appId = params.getString("appId");               // 应用ID
        String mchOrderNo = params.getString("mchOrderNo"); 	// 商户订单号
        String channelId = params.getString("channelId"); 	    // 渠道ID
        String passageId = params.getString("passageId");       // 通道ID
        String amount = params.getString("amount"); 		    // 支付金额（单位分）
        String currency = params.getString("currency");         // 币种
        String clientIp = params.getString("clientIp");	        // 客户端IP
        String device = params.getString("device"); 	        // 设备
        String extra = params.getString("extra");		        // 特定渠道发起时额外参数
        String param1 = params.getString("param1"); 		    // 扩展参数1
        String param2 = params.getString("param2"); 		    // 扩展参数2
        String notifyUrl = params.getString("notifyUrl"); 		// 支付结果异步回调URL
        String returnUrl = params.getString("returnUrl"); 		// 支付结果同步回调URL
        String sign = params.getString("sign"); 				// 签名
        String subject = params.getString("subject");	        // 商品主题
        String body = params.getString("body");	                // 商品描述信息
        // 验证请求参数有效性（必选项）
        Long mchIdL;
        if(StringUtils.isBlank(mchId) || !NumberUtils.isDigits(mchId)) {
            errorMessage = "request params[mchId] error.";
            return errorMessage;
        }
        mchIdL = Long.parseLong(mchId);
        if(StringUtils.isBlank(appId)) {
            errorMessage = "request params[appId] error.";
            return errorMessage;
        }
        if(StringUtils.isBlank(mchOrderNo)) {
            errorMessage = "request params[mchOrderNo] error.";
            return errorMessage;
        }
        if(StringUtils.isBlank(channelId)) {
            errorMessage = "request params[channelId] error.";
            return errorMessage;
        }
        Integer passageIdI = null;
        if(StringUtils.isNotBlank(passageId) && !NumberUtils.isDigits(passageId)) {
            errorMessage = "request params[passageId] error.";
            return errorMessage;
        }
        if(StringUtils.isNotBlank(passageId)) passageIdI = Integer.parseInt(passageId);
        if(!NumberUtils.isNumber(amount)) {
            errorMessage = "request params[amount] error.";
            return errorMessage;
        }
        if(StringUtils.isBlank(currency)) {
            errorMessage = "request params[currency] error.";
            return errorMessage;
        }
        if(StringUtils.isBlank(notifyUrl)) {
            errorMessage = "request params[notifyUrl] error.";
            return errorMessage;
        }
        if(StringUtils.isBlank(returnUrl)) {
            errorMessage = "request params[returnUrl] error.";
            return errorMessage;
        }
        if(StringUtils.isBlank(subject)) {
            errorMessage = "request params[subject] error.";
            return errorMessage;
        }
        if(StringUtils.isBlank(body)) {
            errorMessage = "request params[body] error.";
            return errorMessage;
        }
        String channelUser = "";
        // 根据不同渠道,判断extra参数
        if(PayConstant.PAY_CHANNEL_WX_JSAPI.equalsIgnoreCase(channelId)) {
            if(StringUtils.isEmpty(extra)) {
                errorMessage = "request params[extra] error.";
                return errorMessage;
            }
            JSONObject extraObject = JSON.parseObject(extra);
            String openId = extraObject.getString("openId");
            if(StringUtils.isBlank(openId)) {
                errorMessage = "request params[extra.openId] error.";
                return errorMessage;
            }
            channelUser = openId;
        }else if(PayConstant.PAY_CHANNEL_WX_NATIVE.equalsIgnoreCase(channelId)) {
            if(StringUtils.isEmpty(extra)) {
                errorMessage = "request params[extra] error.";
                return errorMessage;
            }
            JSONObject extraObject = JSON.parseObject(extra);
            String productId = extraObject.getString("productId");
            if(StringUtils.isBlank(productId)) {
                errorMessage = "request params[extra.productId] error.";
                return errorMessage;
            }
        }else if(PayConstant.PAY_CHANNEL_WX_MWEB.equalsIgnoreCase(channelId)) {
            if(StringUtils.isEmpty(extra)) {
                errorMessage = "request params[extra] error.";
                return errorMessage;
            }
            JSONObject extraObject = JSON.parseObject(extra);
            String productId = extraObject.getString("sceneInfo");
            if(StringUtils.isBlank(productId)) {
                errorMessage = "request params[extra.sceneInfo] error.";
                return errorMessage;
            }
            if(StringUtils.isBlank(clientIp)) {
                errorMessage = "request params[clientIp] error.";
                return errorMessage;
            }
        }else if(PayConstant.PAY_CHANNEL_ACCOUNTPAY_BALANCE.equalsIgnoreCase(channelId)) {
            if(StringUtils.isEmpty(extra)) {
                errorMessage = "request params[extra] error.";
                return errorMessage;
            }
            JSONObject extraObject = JSON.parseObject(extra);
            String userId = extraObject.getString("userId");
            if(StringUtils.isBlank(userId)) {
                errorMessage = "request params[extra.userId] error.";
                return errorMessage;
            }
            channelUser = userId;
        }

        // 签名信息
        if (StringUtils.isEmpty(sign)) {
            errorMessage = "request params[sign] error.";
            return errorMessage;
        }

        // 查询商户信息
        MchInfo mchInfo = rpcCommonService.rpcMchInfoService.findByMchId(mchIdL);
        if(mchInfo == null) {
            errorMessage = "Can't found mchInfo[mchId="+mchId+"] record in db.";
            return errorMessage;
        }
        if(mchInfo.getState() != MchConstant.PUB_YES) {
            errorMessage = "mchInfo not available [mchId="+mchId+"] record in db.";
            return errorMessage;
        }

        // 查询应用信息
        MchApp mchApp = rpcCommonService.rpcMchAppService.findByMchIdAndAppId(mchIdL, appId);
        if(mchApp == null) {
            errorMessage = "Can't found app[appId="+appId+"] record in db.";
            return errorMessage;
        }
        if(mchApp.getState() != MchConstant.PUB_YES) {
            errorMessage = "app not available [appId="+appId+"] record in db.";
            return errorMessage;
        }

        String key = mchInfo.getPrivateKey();
        if (StringUtils.isBlank(key)) {
            errorMessage = "key is null[mchId="+mchId+"] record in db.";
            return errorMessage;
        }
        payContext.put("key", key);

        // 查询商户对应的支付渠道
        String channelMchId = null;
        String channelType = null;
        BigDecimal channelRate = null;
        // 平台账户
        if(mchInfo.getKind() == MchConstant.MCH_TYPE_PLATFORM) {
            PayChannel payChannel;
            if(passageId == null) {
                errorMessage = "request params[passageId] error.";
                return errorMessage;
            }else {
                // 如果下单时没有指定渠道ID,则临时取一个
                payChannel = rpcCommonService.rpcPayChannelService.findByChannelId(channelId);
            }
            if(payChannel != null) {
                channelMchId = payChannel.getChannelMchId();
                channelType = payChannel.getChannelType();
                channelRate = payChannel.getChannelRate();
            }

        }else if(mchInfo.getKind() == MchConstant.MCH_TYPE_PRIVATE) { // 私用账户
            MchChannel mchChannel = rpcCommonService.rpcMchChannelService.findByMACId(mchIdL, appId, channelId);
            if(mchChannel != null) {
                channelMchId = mchChannel.getChannelMchId();
                channelType = mchChannel.getChannelType();
                channelRate = mchChannel.getChannelRate();
            }
        }

        if(channelType == null) {
            errorMessage = "Can't found mchChannel[channelId="+channelId+",mchId="+mchId+"] record in db.";
            return errorMessage;
        }

        // 验证签名数据
        boolean verifyFlag = PayUtil.verifyPaySign(params, key);
        if(!verifyFlag) {
            errorMessage = "Verify XX pay sign failed.";
            return errorMessage;
        }
        // 验证参数通过,返回JSONObject对象
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(MySeq.getPay());
        payOrder.setMchId(mchIdL);
        payOrder.setMchType(mchInfo.getKind());
        payOrder.setAppId(appId);
        payOrder.setMchOrderNo(mchOrderNo);
        payOrder.setChannelType(channelType);
        payOrder.setChannelId(channelId);
        payOrder.setPassageId(passageIdI);
        payOrder.setAmount(Long.parseLong(amount));
        payOrder.setCurrency(currency);
        payOrder.setClientIp(clientIp);
        payOrder.setDevice(device);
        payOrder.setTitle(subject);
        payOrder.setBody(body);
        payOrder.setExtra(extra);
        payOrder.setChannelMchId(channelMchId);
        payOrder.setChannelUser(channelUser);
        payOrder.setParam1(param1);
        payOrder.setParam2(param2);
        payOrder.setNotifyUrl(notifyUrl);
        payOrder.setReturnUrl(returnUrl);
        // 商户为平台账户,设置商户费率 平台利润 渠道费率 渠道成本
        if(mchInfo.getKind() == MchConstant.MCH_TYPE_PLATFORM) {
            // 商户费率
            BigDecimal mchRate = mchInfo.getMchRate();
            // 如果设置了商户通道的商户费率,则使用,否则使用商户信息表中的费率
            if(passageIdI != null) {
                MchPassage mchPassage = rpcCommonService.rpcMchPassageService.findByMchIdAndPassageId(mchIdL, passageIdI);
                if(mchPassage != null && mchPassage.getMchRate() != null) {
                    mchRate = mchPassage.getMchRate();
                }
            }
            payOrder.setMchRate(mchRate == null ? new BigDecimal(0.00) : mchRate);
            // 平台利润=订单总额*商户费率/100
            Long platProfit = new BigDecimal(payOrder.getAmount()).multiply(payOrder.getMchRate()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).longValue();
            payOrder.setPlatProfit(platProfit);
            // 渠道费率
            payOrder.setChannelRate(channelRate == null ? new BigDecimal(0.00) : channelRate);
            // 渠道成本=订单总额*渠道费率/100
            Long channelCost = new BigDecimal(payOrder.getAmount()).multiply(payOrder.getChannelRate()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP).longValue();
            payOrder.setChannelCost(channelCost);
        }

        return payOrder;
    }

}
